注册表用法
注册表的配置数据主要在四个时间点被修改。
- 由于引导配置数据库(BCD)存在于注册表储巢中,所以在初始化引导过程中,引导加载器需要读入配置信息和引导设备驱动程序的列表。
- 在内核引导的过程中,内核要读取系统的相关设置信息(存放在注册表中),这些信息包含了加载的驱动程序,系统组件,已经系统行为的配置。
- 在windows登录过程中,Explorer和windows组件读取用户配置信息。
- 应用程序启动过程中,读取系统全局配置信息。
- 其他时间:在调用API修改注册表的时候,注册表也提供异步回调机制,这是优先接收注册表变化的方法。系统不建议使用轮询的方法检测注册表的修改。
注册表的数据类型
注册表的结构类似于一个文件系统的结构。只不过注册表包含的是键和值,一般的我们把键看做目录,值看做文件。例如\trade\mark中,trade表示的是键,mark表示的是值。
注册表的值有多种数据类型,常见的数据类型是REG_DWORD(存储大部分的整形和布尔类型),REG_BINARY(存储的是超过32位的整数类型,例如加密口令),REG_SZ(存储的是字符串)。其中还有特殊的是REG_LINK,REG_LINK存储的是一个链接类型,标明这个数据类型可以让一个键指向另一个键。例如\ROOT1\LINK指向的是ROOT2\REGKEY,其中REGKEY存储的值是REGVALUE,则对于REGVALUE有两个路径可以访问:\ROOT1\LINK\REGVALUE或者\ROOT2\REGKEY\REGVALUE。windows中六个注册表根有三个是链接,他们指向的是三个非连接的跟腱中的子健。
注册表的逻辑结构
HKEY_CURRENT_USER:
存储和当前登录用户相关的数据(包含用户参数和软件配置信息),指向当前登录用户的用户轮廓,位于磁盘下的\Users\用户名\Ntuser.dat文件中。是HKEY_USERS下对应的当前登录用户的子健。**当一个用户登录系统,首先会创建HKCU,然后把HKCU映射到HKEY_USERS/
下面是关于HKEY_CURRENT_USER的子健信息。
HKEY_USERS:
包含所有已加载用户轮廓的子健,他不是一个链接。但是他包含了一个名为HKU.DEFAULTD的链接。当有新用户登录的时候,系统依赖Users\Default的轮廓为该用户创建一个新的轮廓。
注册表HKLM\Software\Microsoft\Windows Nt\CurrentVersion\ProfileList\ProfilesDirectory中的ProfilesDirectory保存的是系统中存在的用户轮廓列表。每个轮廓存放在ProfileList键下面。
HKEY_CLASS_ROOT:
包含的是文件关联和COM注册信息以及UAC虚拟化注册表根。其中HKCR下的根键保存的是文件拓展名关联,UAC虚拟化注册表位于VirtualStore键下面,其他键包含的是注册在系统中的COM对象的详细配置信息
HKCR是由HKLM\SOFTWARE\Classes(系统全局)和HKEY_USERS\
HKEY_LOCAL_MACHINE:
包含了机器的全局设置,包括了BCD00000000,COMPONENTS,HARDWARE,SAM,SECURITY,SOFTWARE,SYSTEM,该注册表不是一个链接。
HKLM\BCD0000000保存的是引导配置数据库,这是作为一个注册表储巢加载进来的。
HKLM\CONPONENTS保存的是关于CBS(基于组件的服务),当系统组件需要更新,安装或者卸载的时候,保存在这个键下的信息将要被用到。当然为了优化系统资源,CBS栈在服务一个请求的时候只是被动态的加载到系统。
\HKLM\HARDWARE维护了当前系统中的遗留硬件,以及一些硬件设备到其驱动程序的映射关系描述信息。
HKLM\SAM存放的是用户账户和组的信息,SAM的安全描述符一般被设置成管理员无法访问(system可以访问)
HKLM\SECURITY存放的是系统全局的安全设置和用户权限分配,HKLM\SAM是HKLM\SECURITY\SAM下的SERURITY子键,默认情况下无法查看HKLM\SECURITY和HKLM\SAM的内容
HKLM\OTFWARE存放是系统全局配置信息和第三方应用程序的全局设置。
HKLM\SYSTEM保存的是引导系统所需要的全局信息,例如下载的驱动程序或者启动的服务。这些信息对于系统的启动十分重要。
HKEY_CURRENT_CONFIG:
保存的是当前的硬件配置信息,是HKLM\SYSTEM\CurrentControlSet\Hardware Profiles\Current下的子健,
HKEY_PERFORMACHE_DATA:
该项是一个性能计数器,不是一个链接。在注册表编辑器中无法找到,只有试用RegQueryValueEx函数去获取性能信息。性能信息并不是保存在注册表中,RegQueryValueEx函数只是利用该键获得从性能数据提供者那里提供的信息
事务性注册表
事务性注册表的封闭性:当使用RegCreateKeyTransacted以实物的方式创建一个注册表的时候,之后的所有操作必须是以事物方式运行,而且此过程是对事物外部不可见。
事物的隔离性:事物是相互隔离的,在一个事物内部所做的修改,在该事物被提交之前,在该事物外部是不可见的。
事物的低优先级:一个非事物任务的写会导致事物内部的写操作失败。
提交生效:事物只有提交之后才会立即生效。
注册表的内部机理
储巢
注册表是一组称为储巢的独立文件,每一个储巢包含了一个注册表数,有一个键作为该树的根,子键或者值存储在根的下面,但是注册表编辑器所显示的根键和储巢里面的根键不是相互关联的。除了用户轮廓,其他轮廓路径都被编码进了配置管理器中,当加载储巢的时候,配置管理器会在HKLM\SYSTEM\CurrentControlSet\Control\Hivelist子键下记录每个储巢的路径。并将这些储巢链接起来,以便建立其注册表结构。
如下图是注册表路径和储巢路径对应图,其中有一项是易失储巢。标明这个储巢是易变的,并没有对应的文件,由操作系统在内存中加载并且管理。其中著名的例子就是HKLM\HARDWARE.这个储巢保存的系统物理设备和设备资源的信息。每次引导的时候会进行资源分配和硬件检测。
注册表符号链接
注册表符号链接一个种使配置管理器将键与键链接起来的特殊键,符号链接在外部是利用CreateRegKey函数指定REG_GREATE_LINK标志创建起来的,
储巢结构
首先,我们从全局来把握一下注册表的储巢结构,在了解储巢结构之前,我们需要了解一下几个概念。
块:配置管理器从逻辑上将一个储巢分成一些称为块的分配单元。每个块的大小是4096字节。
基本块:一个储巢的第一个块称为基本块,基本块包含了有关该储巢的全局信息。
巢室:一个巢室包含了所存储的注册表键的基本数据结构,之所以称为巢室,是因为巢室是存储注册表键的内容的容器。一个巢室包含了一个键,一个值,一个安全描述符,一列子键,一列子值。
巢箱:新巢室正好拓展到下一个块或者页面的大小,说白了就是巢室的箱子(多个巢室组成了巢箱)。
巢室索引: 详见巢室映射表
巢室开始的四个字节表示的是该巢室数据类型。一共有五种巢室的基本数据类型:键巢室,值巢室,子键列表巢室,值列表巢室,安全描述符巢室。
- 键巢室:包含了一个注册表键的巢室
- 值巢室:包含了一个键的值得巢室
- 子键列表巢室:包含了许多子键巢室的索引的巢室
- 值列表巢室:包含了许多值巢室的索引的巢室,是父键的所有值
- 安全描述符巢室:包含了一个安全描述符的巢室
windows选择利用巢箱的结构来拓展巢室,当一个巢室要被添加到储巢的时候,系统创建一个巢箱来容纳巢室,这导致了系统分配和释放巢箱的频率比使用巢室要慢的多。
配置管理器会将储巢整个读入,同时也会象磁盘碎片管理的方式,合并诸多空巢箱,当储巢尾部的巢箱变为空闲的时候才会缩短该储巢。
巢室映射表
注册表被映射如内存是不连续的,配置管理器采用的是和虚拟内存映射到物理内存地址的策略。和windows的分段内存管理机制一样,windows使用连续的内存代表一个巢箱,这样一个巢箱内部所有的巢室都出现在一个视图中。
windows使用巢室索引来实现映射策略,巢室索引有三个域:目录索引,表索引,字节偏移。
【重点】①windows将目录索引解释成一个巢室的映射表目录的索引,也就是说根据这个索引才能找到巢室映射目录表的地址。②巢室映射表目录有1024个,每个目录项指向的是一个映射表,利用第二个域–表索引,根据巢室映射表找到巢室所在巢箱的偏移,③根据第三个域找到巢室的偏移。
注册表名字空间和操作
键对象:当打开或创建一个注册表键的时候,对象管理器会给应用程序分配一个句柄让程序通过此句柄引用该键。
键控制块:每次打开注册表键的时候,配置管理器也会分配一个键控制块,用于保存键名称,巢室索引,和一个标志(表示键的句柄被关闭后,配置管理器是否应该删除该键句柄所引用的键巢室)
关系:键对象指向他对应的键控制块,如果两个应用程序打开一个注册表键的时候,会产生两个不同的键对象,这两个键对象指向同一个键控制块,(因为键控制块包含了键的名称等唯一信息)。
利用键控制块的引用个数来决定是否删除该键控制块,如果引用计数为0的时候,说明这个键控制块不再被需要了。
【实验】查看键控制块
- !reg openkeys:查看系统中已经被分配的键控制块。
- !reg findkcb:查看一个已经打开的kcb
- !reg kcb:查看具体的kcb内容
稳定可靠的存储
使用双日志方案,将脏数据累加写入同一个日志文件,如果不发生错误,只写入某一个文件。
对于写脏数据到日志文件,使用了双序列号模式,延时写出器首先更新日志文件一个序列号,然后在写入脏数据,此时发生意外,系统在引导时,配置管理器肯定会注意到序列号不一致,触发储巢回滚。如果写入日志成功,在写另一个序列号。保证两个序列号一致。